package com.kangtushizhe.config;

import com.kangtushizhe.filter.JwtCheckBeforeAuthorizationFilter;
import com.kangtushizhe.filter.JwtCheckBeforeLogoutFilter;
import com.kangtushizhe.handler.*;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;

@Configuration
@EnableWebSecurity
/*prePostEnabled-开启注解@PreAuthorize和 @PostAuthorize注解。用于基于表达式字符串的权限管理表达式字符串，就是 access方法的参数字符串
表达式，比如"hasRole('ROLE_角色名称')"
securedEnabled-开启注解@secured 功能。用于基于角色的权限管理
注意，在低版本security框架中，此注解不开启，直接使用@secured 等注解时，启动错误。|*/
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)//启用全局方法注解安全
//其中DefaultSecurityFilterChain，用于提供servlet技术中的过滤器链。HttpSecurity，用于提供security框架中的配置处理。l
public class SecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    @Resource
    private AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;
    @Resource
    private AppAuthenticationFailHandler appAuthenticationFailHandler;
    @Resource
    private AppLogoutSuccessHandler appLogoutSuccessHandler;
    @Resource
    private AppAccessDenyHandler appAccessDenyHandler;
    @Resource
    private JwtCheckBeforeAuthorizationFilter jwtCheckBeforeAuthorizationFilter;
    @Resource
    private JwtCheckBeforeLogoutFilter jwtCheckBeforeLogoutFilter;
    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
        Customizer<FormLoginConfigurer<HttpSecurity>> loginConfigurerCustomizer = new Customizer<FormLoginConfigurer<HttpSecurity>>() {
            @Override
            public void customize(FormLoginConfigurer<HttpSecurity> configurer) {
                configurer
                        .loginProcessingUrl("/userLogin")//登录请求地址 为post请求
                        .usernameParameter("username")//自定义用户名形参名
                        .passwordParameter("password")//自定义密码形参名
                        .successHandler(appAuthenticationSuccessHandler) //认证成功后处理逻辑(返回json字符串)
                        .failureHandler(appAuthenticationFailHandler) //认证失败后处理逻辑(返回json字符串)
                ;
            }
        };

        Customizer<RememberMeConfigurer<HttpSecurity>> rememberMe = new Customizer<RememberMeConfigurer<HttpSecurity>>() {
            @Override
            public void customize(RememberMeConfigurer<HttpSecurity> configurer) {
//                configurer
//                        .tokenRepository(persistentTokenRepository())  //保存记住我数据的具体对象
//                        .rememberMeParameter("remember-me") //请求参数中，记住我参数名，默认remember-me
//                        .rememberMeCookieName("BJSXT_REA") //记住我服务器端把登录数据保存在数据库，客户端通过cookie记录数据库
//                        .rememberMeCookieDomain("localhost") // cookie 的domain,默认自动解析
//                        .tokenValiditySeconds(18000) //cookie保存时长，单位是秒。
//                        .userDetailsService(userDetailsService) // 设置自定义userDetailsService接口实现对象
                ;
            }
        };

        Customizer<LogoutConfigurer<HttpSecurity>> logout = new Customizer<LogoutConfigurer<HttpSecurity>>() {
            @Override
            public void customize(LogoutConfigurer<HttpSecurity> configurer) {
                configurer
                        .logoutUrl("/userLogout")//退出登录请求地址，默认是/Logout
                        .logoutSuccessHandler(appLogoutSuccessHandler);//退出成功后的处理代码返回json
                ;
            }
        };

        Customizer<ExceptionHandlingConfigurer<HttpSecurity>> handlingConfigurerCustomizer = new Customizer<ExceptionHandlingConfigurer<HttpSecurity>>() {
            @Override
            public void customize(ExceptionHandlingConfigurer<HttpSecurity> httpSecurityExceptionHandlingConfigurer) {
//                设置访问无权限处理器
                httpSecurityExceptionHandlingConfigurer
                        .accessDeniedHandler(appAccessDenyHandler);
            }
        };


        //关闭csrf
        security.csrf().disable();
        //允许跨域
        security.cors();
        //使用jwt从而禁用session
        security.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        security.formLogin(loginConfigurerCustomizer);
        security.rememberMe(rememberMe);
        security.logout(logout);
        security.exceptionHandling(handlingConfigurerCustomizer);
        security.addFilterBefore(jwtCheckBeforeAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
        security.addFilterBefore(jwtCheckBeforeLogoutFilter, LogoutFilter.class);
        security.authorizeRequests()
                /*
                anonymous -必须未登录才可以访问
                authenticated -必须已登录才可以访问，包含rememberMe登录方式
                fullyAuthenticated -必须完整登录才可以访问，不可以使用rememberMe方式登录，敏感操作中使用，如消费，改密码
                rememberMe -必须使用rememberMe方式才可以访问
                permitAll -无权限校验，随意访问
                denyAll -不可以访间
                */
//                .requestMatchers("/userLogout").permitAll() //登录请求可以直接访问
//                .requestMatchers("/swagger-ui/index.html").permitAll() //登录请求可以直接访问
                .anyRequest().permitAll() //
//                .anyRequest().authenticated()  //其他请求必须先登录
        ;
        security.formLogin().permitAll();//登录开启

        return security.build();
    }

    /*
     * 创建一个Bean对象
     * 这个bean对象，使用security框架提供的实现类型即可。
     * JdbcTokenRepositoryImpl - 基于Datasource数据源连接池，访问指定的数据库
     * 把认证成功的，需要rememberMe的用户数据保存到数据库
     */
    /*
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl repository =
                new JdbcTokenRepositoryImpl();
        repository.setDataSource(dataSource);
        //初始化参数，仅第一次启动当前项目时设置为true。后续再次启动修改false
        //如果设置为true，启动时，自动连接数据库，创建表格，保存rememberMe数据
        repository.setCreateTableOnStartup(false);
        return repository;
    }
    */
    //    ---------------------------------------------------------------------------------------------------------------


}

